/*
 *  WordNet.cpp
 *  WordNet
 *
 *  Created by Kutlak Roman on 28/08/2011.
 *  Copyright 2011 University of Aberdeen. All rights reserved.
 *
 */

#include <iostream>

#include "WordNet.h"

using std::string;
using std::cerr;
using std::cout;
using std::endl;

namespace kutlak {
namespace roman {
namespace gre {
    
    // undecided POS
    int ANY = 123456;
    
    // class implemented
    /////////////////////////////// PUBLIC   ///////////////////////////////////
#pragma mark -
#pragma mark lifecycle
    
    WordNet& WordNet::Instance()
    {
        static WordNet sInstance;
        return sInstance;
    }
    
    WordNet::WordNet()
    { wninit(); }
    
    WordNet::~WordNet() = default;
    
#pragma mark -
#pragma mark operations
    //=============================  OPERATIONS  ===============================
    
    StringVector_t WordNet::Synonyms(const string& word)
    {
        StringVector_t result;
        for (int pos = 1; pos <= NUMPARTS; pos++)
        {
            StringVector_t tmp = Synonyms(word, pos);
            std::move(tmp.begin(), tmp.end(), std::back_inserter(result));
        }
        
        return result;
    }
    
    StringVector_t WordNet::Synonyms(const string& word, int pos)
    {
        StringVector_t result;
        
        // findtheinfo_ds(word, POS, type_of_search, sense);
        char* searchWord = 
        strtolower(strsubst(const_cast<char*>(word.c_str()), ' ', '_'));
        SynsetPtr synset = findtheinfo_ds(searchWord, pos, HYPERPTR, ALLSENSES);
        SynsetPtr head = synset;
        
        // iterate through senses
        while (synset)
        {   
            // go through the words in this synset
            for (int wordIndex = 0; wordIndex < synset->wcount; ++wordIndex)
            {
                string tmpWord = string(synset->words[wordIndex]);
                result.push_back(std::move(tmpWord));
            }
            synset = synset->nextss;
        }
        
        if (head)
            free_syns(head);
        
        return result;
    }
    
    StringVector_t WordNet::Synonyms(const StringVector_t& words, int pos)
    {
        StringVector_t result;
        for(auto word : words)
        {
            auto tmp = Synonyms(word, pos);
            std::move(tmp.begin(), tmp.end(), std::back_inserter(result));
        }
        
        std::sort(result.begin(), result.end());
        auto mark = std::unique(result.begin(), result.end());
        result.resize(mark - result.begin());
        
        return result;
    }
    
    
    StringVector_t WordNet::Hypernyms(const string& word, int pos)
    {
        StringVector_t result;
        
        // findtheinfo_ds(word, POS, type_of_search, sense);
        char* searchWord = 
        strtolower(strsubst(const_cast<char*>(word.c_str()), ' ', '_'));
        SynsetPtr synset = findtheinfo_ds(searchWord, pos, -HYPERPTR, ALLSENSES);
        SynsetPtr head = synset;
        
        while (synset)
        {   
            SynsetPtr sense = synset;
            while (sense)
            {
                if (sense->whichword < sense->wcount)
                {
                    string tmpWord = string(sense->words[0]);
                    result.push_back(std::move(tmpWord));
                }
                
                sense = sense->ptrlist;
            }
            
            synset = synset->nextss;
        }
        
        if (head)
            free_syns(head);
        
        return result;
    }
    
    StringVector_t WordNet::Antonyms(const string& word)
    {
        StringVector_t result;
        for (int pos = 1; pos <= NUMPARTS; pos++)
        {
            auto tmp = Antonyms(word, pos);
            std::move(tmp.begin(), tmp.end(), std::back_inserter(result));
        }
        
        
        std::sort(result.begin(), result.end());
        auto mark = std::unique(result.begin(), result.end());
        result.resize(mark - result.begin());
        
        return result;
    }
    
    StringVector_t WordNet::Antonyms(const string& word, int pos)
    {
        StringVector_t result;
        
        char* searchWord = 
        strtolower(strsubst(const_cast<char*>(word.c_str()), ' ', '_'));
        SynsetPtr synsets = findtheinfo_ds(searchWord, pos, ANTPTR, ALLSENSES);
        SynsetPtr head = synsets;
        
        while (synsets) {
            
            SynsetPtr sense = synsets->ptrlist;
            while (sense)
            {
                for (int wordIndex = 0; wordIndex < sense->wcount; ++wordIndex)
                {
                    string tmpWord = string(sense->words[wordIndex]);
                    result.push_back(std::move(tmpWord));
                }
                
                sense = sense->ptrlist;
            }
            
            synsets = synsets->nextss;
        }
        
        if (head)
            free_syns(head);
        
        return result;
    }
    
    StringVector_t WordNet::Antonyms(const StringVector_t& words, int pos)
    {
        StringVector_t result;
        
        for (auto word : words)
        {
            auto tmp = Antonyms(word, pos);
            std::move(tmp.begin(), tmp.end(), std::back_inserter(result));
        }
        
        std::sort(result.begin(), result.end());
        auto mark = std::unique(result.begin(), result.end());
        result.resize(mark - result.begin());
        
        return result;
    }
    
    void WordNet::PrintWords(const StringVector_t& words, int indent)
    {
        for_each(words.begin(), words.end(), [](const std::string& word)
        {
            cout << word << endl;
        });
        
        cout << endl;
    }
    
    /*
    SetT WordNet::ResultToList(const ResultsetT& words, int senses)
    {
        SetT result;
        SetT::const_iterator included;
        int i = 0;
        
        for (ResultsetT::const_iterator sense = words.begin();
             sense != words.end(); ++sense) {
            for (SetT::const_iterator word = sense->begin();
                 word != sense->end(); ++word) {
                bool insert = true;
                for (included = result.begin(); 
                     included != result.end(); ++included)
                {
                    if (*included == *word)
                        insert = false;
                }
                
                if (insert)
                    result.push_back(*word);
            }
            
            if (senses)
                if (++i >= senses)
                    break;
        }
        
        return result;
    }
    */
    
    int WordNet::FindPos(const string& word)
    { return getpos(const_cast<char*>(word.c_str())); }
    
}
}
}
